#include "pch.h"
#include "Actor.h"
#include "RendererEngine.h"
#include "Camera.h"
#include "Engine.h"
#include "Model.h"
namespace mini
{
	CActor::CActor()
	{
		mPosition = CVector(0, 0, 0);
		mScale = CVector(1, 1, 1);
		mRotation = CVector(0, 0, 0);
		mType = Actor;
	}


	CActor::~CActor()
	{
		for (auto item : mpUnits)
		{
			SafeRelease(item);
		}
		mpUnits.clear();
		SafeDelete(mpSkeleton);
		SafeRelease(mpModel);
	}
	
	bool CActor::operator==(CActor& actor)
	{
		return this->getUUID() == actor.getUUID() ? true : false;
	}

	bool CActor::initialise(const std::string& fileName, const std::string& materilaName, ResType type)
	{
		GEngine->stopRender();
		ClearMeshUnits();
		
		if (fileName != "null")
		{
			auto func = [&](CMeshUnit* pMeshUnit)
			{
				mpUnits.push_back(pMeshUnit);
				mpMeshUnits.push_back(pMeshUnit);
				pMeshUnit->buildMaterialUnit(materilaName);
				mpMaterialUnits.push_back(pMeshUnit->getMaterialUnit());
			};
			CResourceBase* pResource = GResourceManager->createResource(fileName, type);
			if (pResource->getResourceType() == Mesh)
			{
				CMeshUnit* pMeshUnit = new CMeshUnit();
				pMeshUnit->bindMesh(dynamic_cast<CMesh*>(pResource));
				func(pMeshUnit);
			}
			else if( pResource->getResourceType() == Model)
			{
				CModel* pModel = dynamic_cast<CModel*>(pResource);
				if(nullptr != pModel)
				{
					int i = 0;
					for(auto item : pModel->getMeshInfoMap() )
					{
						auto pMeshUnit = (item.second).pMeshUnit;
						func(pMeshUnit);
						auto pMaterialUnit = pMeshUnit->getMaterialUnit();
						auto textures = pModel->getGroundTextures(i++);
						int nNumber = 0;
						for (auto item : textures)
						{
							pMaterialUnit->insertTexture(item.second, nNumber++);
						}
					}
					mpModel = pModel;
					mpModel->AddRef();
					SafeRelease(pModel);
				}
			}
		}
		initialiseFinished();
		GEngine->recoverRender();
		return true;
	}


	bool CActor::initialise(const std::string& fileName, ResType type)
	{
		//CResourceBase* pResource = GResourceManager->createResource(fileName, type);
		//if (Model == type)
		//{
		//	mpModel = dynamic_cast<CModel*>(pResource);
		//	if (nullptr != mpModel)
		//	{
		//		auto meshMap = mpModel->getMeshInfoMap();
		//		for (auto& item : meshMap)
		//		{
		//			mpUnits.push_back(item.second.pMeshUnit);
		//		}
		//	}
		//	initialiseFinished();
		//}
		return true;
	}

	bool CActor::loadAnimation(const std::string& fileName)
	{
		auto pRenderer = mini::CRendererEngine::getSinglePtr();
		if (nullptr != pRenderer)
		{
			pRenderer->addRenderer(mini::RendererType::SkinRenderer);
		}
		if (nullptr == mpSkeleton)
		{
			mpSkeleton = new CSkeleton;
			
			if (mpSkeleton->loadSdkMeshAnimation(fileName, mpModel->getSdkMeshData()))
			{
				for (auto item : mpMeshUnits)
				{
					mpSkeleton->bindTransFormFrameMatrix(dynamic_cast<CSkinMeshUnit*>(item));
				}
			}
		}
		return false;
	}

	bool CActor::initialiseMaterial(const std::string& szfile)
	{
		if (mpMeshUnits.size() > 0)
		{
			for (auto& item : mpMeshUnits)
			{
				item->buildMaterialUnit(szfile);
			}
		}
		else
		{
			CMeshUnit* pMeshUnit = new CMeshUnit();
			mpUnits.push_back(pMeshUnit);
			mpMeshUnits.push_back(pMeshUnit);
			pMeshUnit->buildMaterialUnit(szfile);
		}
		return true;
	}

	void CActor::updateUnitProperty(Property* pProperty)
	{
		for (auto item : mpUnits)
		{
			(*item).updateUnitProperty(pProperty);
		}
	}

	void CActor::setPosition(CVector pt, bool bUpdate)
	{
		CVector offset = pt - mPosition;
		mBoundBox.editorPosition(CPoint3F(&offset.x));
		mPosition = pt;
		if (mpMeshUnits.size() != 0)
		{
			for (auto& item : mpMeshUnits)
			{
				item->setPositon(mPosition);
			}
		}
		if (bUpdate)
		{
			updateMeshUnitMatrix(0);
		}
		else
		{
			mbUpdate = true;
		}
	}

	void CActor::setScale(CVector scale, bool bUpdate)
	{
		mScale = scale;
		if (mpMeshUnits.size() != 0)
		{
			for (auto& item : mpMeshUnits)
			{
				item->setScale(mScale);
			}
		}
		if (bUpdate)
		{
			updateMeshUnitMatrix(0);
		}
		else
		{
			mbUpdate = true;
		}
	}

	void CActor::setRotation(CVector rotation, bool bUpdate)
	{
		mRotation = rotation;
		for( auto& item : mpMeshUnits)
		{
			item->setRotation(mRotation);
		}
		if (bUpdate)
		{
			updateMeshUnitMatrix(0);
		}
		else
		{
			mbUpdate = true;
		}
	}

	void CActor::bindMeshUnit(CMeshUnit* pMeshUnit)
	{
		if (nullptr != pMeshUnit)
		{
			mpMeshUnits.push_back(pMeshUnit);
			mpUnits.push_back(pMeshUnit);
			pMeshUnit->AddRef();
			mBoundBox = pMeshUnit->getBoundBox();
		}
	}

	void CActor::removeMeshUnit(CMeshUnit* pMeshUnit)
	{
		if (nullptr != pMeshUnit)
		{
			auto item = std::find(mpMeshUnits.begin(), mpMeshUnits.end(), pMeshUnit);
			if (item != mpMeshUnits.end())
			{
				size_t length = std::distance(mpMeshUnits.begin(), item);
				auto itemBegin = mpMaterialUnits.begin();
				std::advance(itemBegin, length);

				mpMeshUnits.erase(item);
				mpMaterialUnits.erase(itemBegin);

				pMeshUnit->Release();
			}
		}
	}

	void CActor::removeAllMeshUnit()
	{
		for( auto item : mpMeshUnits )
		{
			auto iter = std::find(mpUnits.begin(), mpUnits.end(), item);
			if (iter != mpUnits.end())
			{
				mpUnits.erase(iter);
			}
		//	SafeRelease(item);
		}
		mpMeshUnits.clear();
	}

	void CActor::hideAllMeshUnit()
	{
	}

	std::list<CMeshUnit*>& CActor::getMeshUnits()
	{
		return mpMeshUnits;
	}

	std::list<CMaterialUnit*>& CActor::getMaterials()
	{
		return mpMaterialUnits;
	}

	CVector& CActor::getPosition()
	{
		return mPosition;
	}

	CVector& CActor::getScale()
	{
		return mScale;
	}

	CVector& CActor::getRotation()
	{
		return mRotation;
	}

	std::pair<bool, float> CActor::isPick(const CVector& v, bool isPickBound /*= true*/)
	{
		if (0 != mpMeshUnits.size() && mType != Gui )
		{
			CVector origin;
			if (isPickBound)
			{
				return std::pair<bool, float>(false, 0.0f);
			}
			else
			{
				float fDist = FLT_MAX - 10.0f;
				bool bResult = false;
				for (auto& item : mpMeshUnits)
				{
					INTERSECTION& result = item->isPick(v, isPickBound);
					if (result.fDist < FLT_MAX - 10.0f)
					{
						if (result.fDist < fDist)
						{
							bResult = true;
							fDist = result.fDist;
						}
					}
				}
				return std::pair<bool, float>(bResult, fDist);
			}
		}
		else
		{
			return std::pair<bool, float>(false, 0.0f);
		}
	}

	std::pair<bool, float> CActor::isPick(const CVector& origin, const CVector& dir, bool isPickBound /*= true*/)
	{
		if (0 != mpMeshUnits.size() && mType != Gui)
		{
			if (isPickBound)
			{
				return std::pair<bool, float>(false, 0.0f);
			}
			else
			{
				float fDist = FLT_MAX;
				bool bResult = false;
				for (auto& item : mpMeshUnits)
				{
					INTERSECTION& result = item->isPick(origin, dir, isPickBound);
					if (result.fDist < FLT_MAX)
					{
						if (result.fDist < fDist)
						{
							bResult = true;
							fDist = result.fDist;
						}
					}
				}
				return std::pair<bool, float>(bResult, fDist);
			}
		}
		else
		{
			return std::pair<bool, float>(false, 0.0f);
		}
	}

	CBoundBox& CActor::getBoundBox()
	{
		return mBoundBox;
	}

	void CActor::showBoundBox()
	{
		for (auto item : mpMeshUnits)
		{
			item->getBoundBox().show();
		}
		mBoundBox.show();
	}

	void CActor::hideBoundBox()
	{
		for (auto item : mpMeshUnits)
		{
			item->getBoundBox().hide();
		}
		mBoundBox.hide();
	}

	void CActor::setBoundBoxShow(bool bValue)
	{
		bValue ? showBoundBox() : hideBoundBox();
	}

	bool CActor::isAllowPick()
	{
		return mbPick;
	}

	void CActor::disablePick()
	{
		mbPick = false;
	}

	void CActor::activePick()
	{
		mbPick = true;
	}

	bool CActor::isAlwayShow()
	{
		return mbAlwayShow;
	}

	void CActor::update(float fDeltaTime)
	{
		updateFrame(fDeltaTime);
		updateMeshUnitMatrix(fDeltaTime);
	}

	void CActor::updateMeshUnitMatrix(float fDelatTime)
	{
		if (mbUpdate)
		{
			for (auto& item : mpUnits)
			{
				(*item).update(fDelatTime);
			}
		}
	}

	void CActor::ClearMeshUnits()
	{
		for (auto item : mpMeshUnits)
		{
			SafeRelease(item);
		}
		mpMeshUnits.clear();
	}

	void CActor::updateFrame(float fDeltaTime)
	{
	}

	void CActor::initialiseFinished()
	{
		CPoint3F fMax = { FLT_MIN,FLT_MIN,FLT_MIN };
		CPoint3F fMin = { FLT_MAX,FLT_MAX,FLT_MAX };

		for (auto unit : mpMeshUnits)
		{
			auto _min = unit->getBoundBox().getMinPos();
			if (_min.x < fMin.x)
			{
				fMin.x = _min.x;
			}
			if (_min.y < fMin.y)
			{
				fMin.y = _min.y;
			}
			if (_min.z < fMin.z)
			{
				fMin.z = _min.z;
			}

			auto _max = unit->getBoundBox().getMaxPos();
			if (_max.x > fMax.x)
			{
				fMax.x = _max.x;
			}
			if (_max.y > fMax.y)
			{
				fMax.y = _max.y;
			}
			if (_max.z > fMax.z)
			{
				fMax.z = _max.z;
			}

			mBoundBox.setBoundBoxRange(fMin, fMax);
			
		}
	}

}
